using UnityEngine;
using System.Collections;

public class RTSCameraControls : MonoBehaviour {
	public int MoveSpeed = 500;
	public int RotationSpeed = 30;
	
	private Transform _myTransform;

    private bool _middleMouseDown;
    private bool _rightMouseDown;
    private bool _qDown;
    private bool _eDown;

    private Vector3 _mousePosition;
    private Vector3 _pivotPoint;
	
	// Use this for initialization
	void Start () {
	}
	
	// Update is called once per frame
    void Update()
    {
        _myTransform = transform;
        RaycastHit hit;
        Vector3 newPosition;
        // Detecting end mouse clicks. 1 = right mouse button, 2 = middle mouse button.
        if (Input.GetMouseButtonUp(2))
        {
            _middleMouseDown = false;
        }
        if (Input.GetMouseButtonUp(1))
        {
            _rightMouseDown = false;
        }
        // Keyboard movement, zoom, and detecting start mouse clicks. 1 = right mouse button, 2 = middle mouse button.
        if (!_middleMouseDown && !_rightMouseDown)
        {
            if (Input.GetKeyUp(KeyCode.Q))
            {
                _qDown = false;
            }
            if (Input.GetKeyUp(KeyCode.E))
            {
                _eDown = false;
            }
            // Keyboard translate
            if (Input.GetKey(KeyCode.W))
            {
                newPosition = _myTransform.position +
                              (new Vector3(_myTransform.forward.x, 0, _myTransform.forward.z).normalized*10*MoveSpeed*
                               Time.deltaTime);
                if (Physics.Raycast(newPosition, _myTransform.forward, out hit))
                {
                    _myTransform.position = newPosition;
                }
            }
            if (Input.GetKey(KeyCode.A))
            {
                newPosition = _myTransform.position +
                              (-new Vector3(_myTransform.right.x, 0, _myTransform.right.z).normalized*10*MoveSpeed*
                               Time.deltaTime);
                if (Physics.Raycast(newPosition, _myTransform.forward, out hit))
                {
                    _myTransform.position = newPosition;
                }
            }
            if (Input.GetKey(KeyCode.S))
            {
                newPosition = _myTransform.position +
                              (-new Vector3(_myTransform.forward.x, 0, _myTransform.forward.z).normalized*10*MoveSpeed*
                               Time.deltaTime);
                if (Physics.Raycast(newPosition, _myTransform.forward, out hit))
                {
                    _myTransform.position = newPosition;
                }
            }
            if (Input.GetKey(KeyCode.D))
            {
                newPosition = _myTransform.position +
                              (new Vector3(_myTransform.right.x, 0, _myTransform.right.z).normalized*10*MoveSpeed*
                               Time.deltaTime);
                if (Physics.Raycast(newPosition, _myTransform.forward, out hit))
                {
                    _myTransform.position = newPosition;
                }
            }
            if (Input.GetKeyDown(KeyCode.Q))
            {
                _qDown = true;
                if (Physics.Raycast(_myTransform.position, _myTransform.forward, out hit))
                {
                    _pivotPoint = hit.point;
                }
            }
            else if (Input.GetKeyDown(KeyCode.E))
            {
                _eDown = true;
                if (Physics.Raycast(_myTransform.position, _myTransform.forward, out hit))
                {
                    _pivotPoint = hit.point;
                }
            }
            // Keyboard rotate
            if (_qDown)
            {
                _myTransform.RotateAround(_pivotPoint, Vector3.up, 10*RotationSpeed*Time.deltaTime);

            }
            if (_eDown)
            {
                _myTransform.RotateAround(_pivotPoint, Vector3.up, -10*RotationSpeed*Time.deltaTime);

            }
            var scroll = Input.GetAxis("Mouse ScrollWheel");
            if (scroll != 0) // back
            {
                if (Physics.Raycast(_myTransform.position, _myTransform.forward, out hit))
                {
                    if (scroll < 0) // back
                    {
                        // Mouse zoom
                        var distanceToHit = (hit.point - _myTransform.position).magnitude;
                        if (distanceToHit < 20)
                        {
                            _myTransform.position -= _myTransform.forward;
                        }
                    }
                }
                if (scroll > 0) // forward
                {
                    var distanceToHit = (hit.point - _myTransform.position).magnitude;
                    if (distanceToHit > 3)
                    {
                        _myTransform.position += _myTransform.forward;
                    }
                }
            }
            if (Input.GetMouseButtonDown(1))
            {
                _rightMouseDown = true;
                _mousePosition = Input.mousePosition;
                if (Physics.Raycast(_myTransform.position, _myTransform.forward, out hit))
                {
                    _pivotPoint = hit.point;
                }
            }
            if (Input.GetMouseButtonDown(2))
            {
                _middleMouseDown = true;
                _mousePosition = Input.mousePosition;
                if (Physics.Raycast(_myTransform.position, _myTransform.forward, out hit))
                {
                    _pivotPoint = hit.point;
                }
            }
        }
        else
        {
            var newMousePosition = Input.mousePosition;
            var shift = (newMousePosition - _mousePosition).normalized;
            if (newMousePosition != _mousePosition)
            {
                // Mouse move. Move in the opposite direction as dragging
                if (_rightMouseDown)
                {
                    var angle = Vector3.Angle(shift, Vector3.up);
                    if (AngleDir(Vector3.up, shift, Vector3.forward) == -1)
                    {
                        angle = -angle;
                    }
                    newPosition = _myTransform.position - (Quaternion.Euler(0, -angle, 0) * (new Vector3(_myTransform.forward.x, 0, _myTransform.forward.z) * 10 * MoveSpeed * Time.deltaTime));
                    if (Physics.Raycast(newPosition, _myTransform.forward, out hit))
                    {
                        _myTransform.position = newPosition;
                    }
                }
                // Mouse rotate
                if (_middleMouseDown)
                {
                    // tilt up
                    if (shift.y > 0 && Vector3.Angle(_myTransform.forward, Vector3.down) < 75f)
                    {
                        _myTransform.RotateAround(_pivotPoint, _myTransform.right, -10 * RotationSpeed * Time.deltaTime);
                    }
                    // tilt down
                    else if (shift.y < 0 && Vector3.Angle(_myTransform.forward, Vector3.down) > 30f)
                    {
                        _myTransform.RotateAround(_pivotPoint, _myTransform.right, 10 * RotationSpeed * Time.deltaTime);
                    }
                    if (shift.x > 0)
                    {
                        _myTransform.RotateAround(_pivotPoint, Vector3.up, 10 * RotationSpeed * Time.deltaTime);
                    }
                    else if (shift.x < 0)
                    {
                        _myTransform.RotateAround(_pivotPoint, Vector3.up, -10 * RotationSpeed * Time.deltaTime);
                    }
                }
                _mousePosition = newMousePosition;
            }
        }
	}

    public float AngleDir(Vector3 fwd, Vector3 targetDir, Vector3 up)
    {
        var perp = Vector3.Cross(fwd, targetDir);
        var dir = Vector3.Dot(perp, up);

        if (dir > 0.0)
        {
            return 1.0f;
        }
        else if (dir < 0.0)
        {
            return -1.0f;
        }
        else
        {
            return 0.0f;
        }
    }
}
